package com.zcf.gameController;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.zcf.common.json.Body;
import com.zcf.gameController.gameUtils.*;
import com.zcf.pojo.*;
import com.zcf.service.MoneyServiceImpl;
import com.zcf.service.UserServiceImpl;
import com.zcf.zcf_uitls.JoinRoom;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.context.ContextLoader;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

/**
 * writer: ZQ
 * Time: 2018-2-20
 * Intent: 推筒子服务端
 */
@CrossOrigin
@ServerEndpoint(value = "/webSocket/game/{userid}")
public class WebSocketBull {
	//用户
	private UserServiceImpl us=(UserServiceImpl) ContextLoader.getCurrentWebApplicationContext().getBean("UserServiceImpl");
	//游戏
	private MoneyServiceImpl gm=(MoneyServiceImpl) ContextLoader.getCurrentWebApplicationContext().getBean("moneyServiceImpl");
	private Gson gson = new GsonBuilder() .setExclusionStrategies(new ExclusionStrategy() {
		@Override
		public boolean shouldSkipField(FieldAttributes f) {
			return false;
		}
		@Override
		public boolean shouldSkipClass(Class<?> incomingClass) {
			return incomingClass == Session.class;
		}
	}) .create();
	public static final Map<String, RoomsParam> rooms = new ConcurrentHashMap<String, RoomsParam>();//房间
	public static final Map<String, String> userMap = new HashMap<String, String>();
	public static Map<String, WebSocketBull> clients = new ConcurrentHashMap<String, WebSocketBull>();
	private static CopyOnWriteArraySet<WebSocketBull> sessions = new CopyOnWriteArraySet<WebSocketBull>();

	private Util_Room uroom = new Util_Room();
	private Session session;
	private RoomsParam myRoom;
	private UserBean user;
	private Msg msgs ;
	private Map<String, Object> data = new HashMap<>();
	public PlayBack back = new PlayBack();
	@OnOpen
	public void connect(@PathParam("userid") Long userid,Session session) throws Exception {
		//通过uid查询出用户信息
		UserBean user = us.selectByid(userid);
		userMap.put(userid+"", null);
		if(user != null){
			if(user.getState() == null || user.getState() != 0){
				Msg msg = new Msg(301, "账号已被冻结，请联系管理员", user.getCustom(), "circular");
				broadcastMe(msg,session,user);
				session.close();
			}else{
				this.user = user;
				this.session = session;
				sessions.add(this);
				WebSocketBull.clients.put(userid+"", this);
				Msg msg = new Msg(200, "推筒子连接成功", null, "lianjie");
				broadcastMe(msg,session,user);
			}
		}else{
			Msg msg = new Msg(302,"用户不存在",null,"circular");
			broadcastMe(msg,session,user);
			session.close();
		}
	}
	@OnClose
	public void disConnect(@PathParam("userid") Long userid, Session session) throws Exception{
		if(userMap.containsKey(userid+"")){
			userMap.remove(userid+"");
		}
		if (WebSocketBull.clients.get(user.getId()+"")!=null){
			WebSocketBull.clients.remove(userid+"");
		}


	}
	/**
	 * @param userid
	 * @param msg
	 * @throws Exception
	 */
	@OnMessage
	public void receiveMsg(@PathParam("userid") Long userid,String msg) throws Exception {
		try{
			JSONObject jsonObject = JSONObject.parseObject(msg);
			System.out.println("接收到用户"+user.getWxname()+jsonObject);
			if (myRoom!=null&&(!jsonObject.getString("type").equals("reconnection"))&&(!jsonObject.getString("type").equals("ping"))){
				if (myRoom.getUsers().get(userid+"")==null){
					msgs = new Msg(300,"已被踢",null,"reconnection");
					broadcastMe(msgs, session, user);
					return;
				}
			}
			data.clear();
			Map<String, UserBean> users;
			//加入房间
			if(jsonObject.getString("type").equals("joinRoom")&&user.getType()!=3){
				String rName = jsonObject.getString("roomName");
				RoomsParam joinroom = rooms.get(rName);
				joinRoom(joinroom);
			}
			//快速加入
			if (jsonObject.getString("type").equals("rapidJoin")&&user.getType()!=3){
				Integer type = jsonObject.getInteger("roomCards");
				List<RoomsParam> roomsParams =rooms.values().stream().filter(roomsParam->roomsParam.getRoomGrade().equals(type)).collect(Collectors.toList());
				Random rand = new Random();
				int size = roomsParams.size();
				int i = rand.nextInt(size);
				RoomsParam joinroom = roomsParams.get(i);
				joinRoom(joinroom);
			}
			//坐下
			if (String.valueOf(jsonObject.get("type")).equals("sitDown")&&user.getType()!=3){
				data.clear();
				Integer seat = jsonObject.getInteger("seat");
				Integer i = myRoom.getPostions().get(seat);
				if (!i.equals(0)) {
					msgs = new Msg(301, "已有人", null, "sitDown");
					broadcastMe(msgs, session, user);
					return;
				}
				myRoom.getLock().lock();
				try{
					if (myRoom.getPostions().contains(userid.intValue())){
						myRoom.getPostions().set(myRoom.getPostions().indexOf(userid.intValue()),0);
					}
					if (!user.getIsReady().equals(0)){
						user.setIsReady(0);
					}
					myRoom.getPostions().set(seat,Integer.valueOf(String.valueOf(user.getId())));
				}catch (Exception e){
					e.printStackTrace();
				}finally {
					myRoom.getLock().unlock();
				}
				user.setType(1);
				user.setPosition(seat);
				data.put("positions",myRoom.getPostions());
				List<UserBean> userBeans = gameLogic.selectRoomUserType(myRoom);
				data.put("users",userBeans);
				data.put("userid" , userid);
				msgs = new Msg(200,"坐下成功",data,"sitDown");
				broadcastMe(msgs, session,user);
				msgs = new Msg(200,"玩家"+user.getWxname()+"坐下",data,"sitDown");
				broadcastB(msgs,session.getId(),myRoom);

			}
			//起身
			if (jsonObject.getString("type").equals("sitUp")&&user.getType()!=3){
				data.clear();
				Integer seat = jsonObject.getInteger("seat");
				user.setPosition(-1);
				user.setType(0);
				msgs = new Msg(200,"起身成功",null,"sitUp");
				broadcastMe(msgs, session,user);
				data = gameLogic.returnMap(data, myRoom, "positions-roomName-isUp-roomPoints-pumping-bankerMoney-setupTime");
				msgs = new Msg(200,"玩家"+user.getWxname()+"起身",data,"sitUp");
				broadcastB(msgs,session.getId(),myRoom);
			}
			//准备
			if(jsonObject.getString("type").equals("prepare")&&myRoom.getIsStart()==0&&user.getType()==1){
				Lock lock = myRoom.getLock();
				lock.lock();
				try {
					user.setIsReady(1);
					data.put("userid",userid);
					msgs = new Msg(200,"准备成功！",data,"prepare");
					broadcastMe(msgs, session,user);
					msgs = new Msg(200,"玩家"+user.getWxname()+"准备成功！",data,"prepare");
					broadcastB(msgs,session.getId(),myRoom);
					List<UserBean> userBeans =myRoom.getUsers().values().stream().filter(userBean->userBean.getType().equals(1)).collect(Collectors.toList());
					Map<Integer, List<UserBean>> collect = userBeans.stream().collect(Collectors.groupingBy(UserBean::getIsReady));
					if (myRoom.getIsStart().equals(0)) {
						chatRoom(userBeans, collect);
					}
				}catch (Exception e){
					System.out.println(e.getMessage());
				}finally {
					lock.unlock();
				}
			}
			//取消准备
			if(jsonObject.getString("type").equals("cancelTo")&&myRoom.getIsStart()==0&&user.getType()!=3){
				Lock lock = myRoom.getLock();
				lock.lock();
				try {
					user.setIsReady(0);
					data.put("userid",userid);
					msgs = new Msg(200,"取消准备！",data,"cancelTo");
					broadcastMe(msgs, session,user);
					msgs = new Msg(200,"玩家"+user.getWxname()+"取消准备！",data,"cancelTo");
					broadcastB(msgs,session.getId(),myRoom);
				}catch (Exception e){
					System.out.println(e.getMessage());
				}finally {
					lock.unlock();
				}
			}
			//庄家连坐
			if ("bankerSitting".equals(jsonObject.getString("type"))&&myRoom.getIsStart()==0&&user.getType()!=3){
				UserBean userBean = myRoom.getUserBean(userid);
				if (userBean!=null){
					if (userBean.getIsOwner()==4){
						if (userBean.getMoney()<myRoom.getBankerMoney()){
							msgs = new Msg(200,"申请上庄玩家，金币不足！",data,"bankerSitting");
							broadcastMe(msgs, session,user);
							return;
						}
						userBean.setIsReady(1);
						userBean.setIsOwner(1);
						myRoom.getLock().lock();
						try {
							myRoom.setSitting(1);
							myRoom.setIsUp(userid);
						}catch (Exception e){
							e.printStackTrace();
						}finally {
							myRoom.getLock().unlock();
						}

						data.clear();
						data.put("userid",userid);
						msgs = new Msg(200,"连庄成功！",data,"bankerSitting");
						broadcastMe(msgs, session,user);
						msgs = new Msg(200,"玩家"+user.getWxname()+"继续连庄！",data,"bankerSitting");
						broadcastB(msgs,session.getId(),myRoom);
						myRoom.getLock().lock();
						try {
							List<UserBean> userBeans =myRoom.getUsers().values().stream().filter(userBeanM->userBeanM.getType().equals(1)).collect(Collectors.toList());
							Map<Integer, List<UserBean>> collect = userBeans.stream().collect(Collectors.groupingBy(UserBean::getIsReady));
							if (myRoom.getIsStart().equals(0)) {
								chatRoom(userBeans, collect);
							}
						}catch (Exception e){
							e.printStackTrace();
						}finally {
							myRoom.getLock().unlock();
						}

					}
				}
			}
			//申请上庄
			if(jsonObject.getString("type").equals("applyBanker")&&myRoom.getIsStart()==2&&myRoom.getTimer()>0&&user.getType()!=3){
				if (user.getMoney()>myRoom.getBankerMoney()){
					user.setIsOwner(1);
					data.put("userid",userid);
					msgs = new Msg(200,"申请成功！",data,"applyBanker");
					broadcastMe(msgs, session,user);
					msgs = new Msg(200,"玩家"+user.getWxname()+"申请上庄！",data,"applyBanker");
					broadcastB(msgs,session.getId(),myRoom);
				}else {
					msgs = new Msg(150,"金币不足！",data,"applyBanker");
					broadcastMe(msgs, session,user);
				}
			}
			//抢庄
			if(jsonObject.getString("type").equals("robZhuang")&&myRoom.getIsStart()==2&&myRoom.getTimer()>0&&user.getType()!=3){
				user.setIsOwner(2);
				data.put("userid",userid);
				msgs = new Msg(200,"抢庄成功！",data,"robZhuang");
				broadcastMe(msgs, session,user);
				msgs = new Msg(200,"玩家"+user.getWxname()+"抢庄成功！",data,"robZhuang");
				broadcastB(msgs,session.getId(),myRoom);
			}
			//不抢庄
			if(jsonObject.getString("type").equals("noRob")&&myRoom.getIsStart()==2&&myRoom.getTimer()>0&&user.getType()!=3){
				user.setIsOwner(3);
				data.put("userid",userid);
				msgs = new Msg(200,"不抢庄！",data,"noRob");
				broadcastMe(msgs, session,user);
				msgs = new Msg(200,"玩家"+user.getWxname()+"不抢庄！",data,"noRob");
				broadcastB(msgs,session.getId(),myRoom);
			}
			//下注
			if(jsonObject.getString("type").equals("betton")&&myRoom.getIsStart()==3&&myRoom.getTimer()>0&&user.getType()!=3){
				if (user.getId().equals(myRoom.getIsUp())){
					msgs = new Msg(300,"庄家不能下注！",null,"betton");
					broadcastMe(msgs, session,user);
					return;
				}
				data.clear();
				Integer area =  jsonObject.getInteger("area");
				Integer money =  jsonObject.getInteger("money");
				if (money==null||area==null){
					msgs = new Msg(300,"参数错误！",null,"betton");
					broadcastMe(msgs, session,user);
					return;
				}
				myRoom.getLock().lock();
				try {
					Map<Integer, Integer> betMap = myRoom.getBetMap();
					Map<Integer, Integer> betMoeny = myRoom.getBetMoeny();
					Integer integer = betMoeny.get(area);
					if (user.getMoney()-money<0){
						msgs = new Msg(300,"金币不足！",null,"betton");
						broadcastMe(msgs, session,user);
						return;
					}

					Integer amount = betMap.get(area);
					if (amount-money<0){
						msgs = new Msg(300,"不可超出下注限制！",null,"betton");
						broadcastMe(msgs, session,user);
						return;
					}
					betMap.put(area,amount-money);
					betMoeny.put(area,money+integer);
					user.setMoney(user.getMoney()-money);
					Integer userBet =user.getBetList().get(area);;
					if (userBet==null){
						user.getBetList().put(area,money);
					}else {
						user.getBetList().put(area,user.getBetList().get(area)+money);
					}
					IntSummaryStatistics intSummaryStatistics = user.getBetList().values().stream().mapToInt((s) -> s).summaryStatistics();
					data.put("userBet",intSummaryStatistics.getSum());
					data.put("areaMoney",myRoom.getBetMoeny().get(area));
					data.put("roomArea",myRoom.getBetMap().get(area));
					data.put("userid",user.getId());
					data.put("area",area);
					data.put("money",money);
					data.put("spareMoeny",user.getMoney());
				}catch (Exception e){
					e.printStackTrace();
				}finally {
					myRoom.getLock().unlock();
				}
				msgs = new Msg(200,"下注成功",data,"betton");
				broadcastMe(msgs, session,user);
				broadcastB(msgs,session.getId(),myRoom);
			}
			//开牌
			if(jsonObject.getString("type").equals("openCard")&&myRoom.getIsStart()==5&&user.getType()!=3){
				Integer position = user.getPosition();
				List<Integer> list = myRoom.getCards().get(position);
				if (list==null){
					msgs = new Msg(200,"明牌失败！",null,"openCard");
					broadcastMe(msgs, session,user);
					return;
				}
				user.setOpenCards(1);
				data.clear();
				data.put("positions", position);
				data.put("personal" , 0);
				data.put("userid",userid);
				data.put("cards" , list);
				msgs = new Msg(200,"明牌成功",data,"openCard");
				broadcast(msgs,myRoom);
				return;

			}

			//退出游戏
			if(jsonObject.getString("type").equals("exitGame")){
				//玩家退出
				uroom.exitGame(myRoom, user,this);
				if (clients.get(user.getId()+"")!=null){
					clients.remove(user.getId()+"");
				}
				this.disConnect(userid,session);
				return;
			}
			//断线重连
			if(jsonObject.getString("type").equals("reconnection")){
				Collection<RoomsParam> values = rooms.values();
				myRoom=null;
				for (RoomsParam roomsParam:values){
					Set<String> strings = roomsParam.getUsers().keySet();
					for (String uid:strings){
						if (uid.equals(userid+"")){
							myRoom=roomsParam;
						}
					}
				}
				if(myRoom==null){
					msgs = new Msg(311,"重连失败",data,"reconnection");
					broadcastMe(msgs, session, user);
					return;
				}
				UserBean userBean = myRoom.getUsers().get(String.valueOf(userid));
				userBean.setSession(this.session);
				this.user = userBean;
				user.setXt(10);
				if (user.getIsReady()==1){
					userBean.setType(1);
				}else {
					userBean.setType(0);
					if (myRoom.getPostions().contains(userid.intValue())){
						int i1 = myRoom.getPostions().indexOf(userid.intValue());
						myRoom.getPostions().set(i1,0);
					}
					user.setPosition(-1);
				}
				data.clear();
				Body body = gm.get(myRoom.getGametype(),myRoom.getRoomGrade());
				if (body.getMeta().getCode()==200){
					data.put("chip",body.getData());
				}
				data.put("room" ,myRoom);
				data.put("myself",user);
				List<UserBean> collect = myRoom.getUsers().values().stream().filter(userBean1 -> userBean1.getType().equals(1)).collect(Collectors.toList());
				data.put("users",collect);
				msgs = new Msg(200,"已重连",data,"reconnection");
				broadcastMe(msgs, this.session, user);
				return;
			}
			//心跳
			if(jsonObject.getString("type").equals("ping")){
				msgs = new Msg(200,"心跳",null,"pong");
				broadcastMe(msgs, session, user);
				user.setXt(10);
				return;
			}
			//观战
			if(jsonObject.getString("type").equals("watching")){
				List<UserBean> collect = myRoom.getUsers().values().stream().filter(userBean -> userBean.getType().equals(0)).collect(Collectors.toList());
				if (collect.size()<1){
					data.put("userid",userid);
					msgs = new Msg(300,"没有观战玩家！",data,"watching");
					broadcastMe(msgs, session,user);
					return;
				}
				data.put("users",collect);
				data.put("userid",userid);
				msgs = new Msg(200,"观战玩家！",data,"watching");
				broadcastMe(msgs, session,user);
			}
		}catch (Exception e){
			e.printStackTrace();
		}

	}
	/**
	* @description :加入房间
	* @param joinroom 
	* @date : 2019/11/12 19:40
	* @author : jj
	*/
	private  void joinRoom(RoomsParam joinroom){
		if (joinroom==null){
			msgs = new Msg(100,"房间不存在",null,"joinRoom");
			broadcastMe(msgs,session,user);
			return;
		}
		if (joinroom.getRoomPoints()>user.getMoney()){
			msgs = new Msg(100,"金币不足",null,"joinRoom");
			broadcastMe(msgs,session,user);
			return;
		}
		msgs = JoinRoom.isBeing(user,data);
		if(msgs!=null){
			broadcastMe(msgs, session,user);
			return;
		}
		user.setSession(session);
		user.setType(0);
		gameLogic.initializeUser(user);
		this.myRoom = joinroom;
		myRoom.getLock().lock();
		try{
			myRoom.getUsers().put(String.valueOf(user.getId()),user);
		}catch (Exception e){
			System.out.println(e.getMessage());
		}finally {
			myRoom.getLock().unlock();
		}
		Body body = gm.get(myRoom.getGametype(),myRoom.getRoomGrade());
		if (body.getMeta().getCode()==200){
			data.put("chip",body.getData());
		}
		data.put("room" ,myRoom);
		data.put("myself",user);
		List<UserBean> collect = myRoom.getUsers().values().stream().filter(userBean -> userBean.getType().equals(1)).collect(Collectors.toList());
		data.put("users",collect);
		msgs = new Msg(200,"加入房间成功",data,"joinRoom");
		broadcastMe(msgs, session,user);
	}

	/**
	* @description :监听房间动态
	* @param userBeans 
	* @param collect
	* @date : 2019/11/19 18:09
	* @author : jj
	*/
	public void chatRoom(List<UserBean> userBeans ,Map<Integer, List<UserBean>> collect){
		if (userBeans.size()==collect.get(1).size()&&userBeans.size()>1){
			myRoom.setIsStart(1);
			data.clear();
			myRoom.setTimer(myRoom.getRoomType().get("setupTime"));
			data.put("setupTime",myRoom.getTimer());
			List<UserBean> userList =myRoom.getUsers().values().stream().filter(userBean->userBean.getType().equals(1)).collect(Collectors.toList());
			data.put("users",userList);
			data.put("isStart",myRoom.getIsStart());
			data.put("positions",myRoom.getPostions());
			data.put("up" , myRoom.getIsUp());
			msgs = new Msg(200,"游戏开始", data,"startGame");
			try {
				broadcast(msgs, myRoom);
			} catch (Exception e) {
				e.printStackTrace();
			}
			myRoom.Initialization();
			CountdownTimer countdownTimer=new CountdownTimer(myRoom.getTimer(),myRoom,this);
			countdownTimer.start();
		}
	}
	/*
	 *@Author:ZhaoQi
	 *@methodName:按照房间名进行广播  发送给所有人
	 *@Params:[roomName, msg]
	 *@Description:
	 *@Return:void
	 *@Date:2018/11/30
	 */
	public synchronized void broadcast(Msg msg,RoomsParam rParam) throws Exception {

		Map<String, UserBean> userMap = rParam.getUsers();
		if(userMap != null){
			for(UserBean m:userMap.values()){
				if(m.getType() != 3 && m.getSession().isOpen() ){
					WebSocketBull.clients.get(m.getId()+"").broadcastMe(msg);
				}
			}
		}

	}

	/**
	 *@Author:ZhaoQi
	 *@methodName:发送消息给除了自己的其他人
	 *@Params:
	 *@Description:
	 *@Return:
	 *@Date:2019/2/14
	 */
	public synchronized void broadcastB(Msg msg, String id,RoomsParam rParam) throws Exception {
		Map<String, UserBean> userMap = rParam.getUsers();
		if(userMap!=null){
			for(UserBean m:userMap.values()){
				if(!m.getSession().getId().equals(id)){
					if(m.getSession().isOpen() && m.getType() != 3){
						System.out.println(msg);
						WebSocketBull.clients.get(m.getId()+"").broadcastMe(msg);
					}
				}
			}
		}

	}
	public synchronized void broadcastMe(Msg msg) {
		System.out.println(msg);
		this.broadcastMe(msg, session, user);
	}
	/*
	 *@Author:ZhaoQi
	 *@methodName:给自己返回消息
	 *@Params:
	 *@Description:
	 *@Return:
	 *@Date:2019/2/14
	 */
	public synchronized void broadcastMe(Msg msg, Session session,UserBean user) {
		String message = gson.toJson(msg);
		if((session.isOpen() && user.getType()!= 3) || user.getType() == null){
			try {
				System.out.println(message);
				session.getBasicRemote().sendText(message);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	@OnError
	public void onError(Throwable t) throws Throwable {
		t.printStackTrace();
	}
}